/*
 * File     : EnumStructureField.java
 * All Rights Reserved.
 */
package com.googlecode.dni.internal.structure;

import static com.googlecode.dni.internal.DniTypes.ENUM_DICTIONARY_TYPE;
import static org.objectweb.asm.Opcodes.*;

import java.util.Map;

import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Type;

import com.googlecode.dni.internal.StructureFieldOrParameter;
import com.googlecode.dni.type.ObjectReference;
import com.googlecode.dni.type.nativetype.NativeInt;
import com.googlecode.dni.type.nativetype.NativeTypes;

/**
 * Encapsulates details of a primitive structure's field used during class
 * generation.
 *
 * @author Matthew Wilson
 */
final class EnumStructureField extends StructureField
{

    private static final PrimitiveType NATIVE_INT = PrimitiveType.getType( NativeInt.class );

    private boolean setDictionaryField;

    private String dictionaryField;


    /** {@inheritDoc} */
    @Override
    String getCType()
    {
        return "enum";
    }

    /** {@inheritDoc} */
    @Override
    int getSize()
    {
        return NativeTypes.NATIVE_INT_SIZE;
    }

    /** {@inheritDoc} */
    @Override
    void writeGetter( final MethodVisitor methodVisitor, final String implName )
    {
        methodVisitor.visitFieldInsn( GETSTATIC,
                                      implName,
                                      this.dictionaryField,
                                      "L" + ENUM_DICTIONARY_TYPE + ";" );

        writeGetPointer( methodVisitor );
        NATIVE_INT.writeGet( methodVisitor, false );

        methodVisitor.visitMethodInsn( INVOKEVIRTUAL,
                                       ENUM_DICTIONARY_TYPE,
                                       "enumForValue",
                                       "(I)Ljava/lang/Enum;" );

        methodVisitor.visitTypeInsn( CHECKCAST, getJavaType().getName().replace( '.', '/' ) );

        methodVisitor.visitInsn( ARETURN );
    }

    /** {@inheritDoc} */
    @Override
    void writeSetter( final MethodVisitor methodVisitor, final String implName )
    {
        writeGetPointer( methodVisitor );

        methodVisitor.visitFieldInsn( GETSTATIC,
                                      implName,
                                      this.dictionaryField,
                                      "L" + ENUM_DICTIONARY_TYPE + ";" );
        methodVisitor.visitVarInsn( ALOAD, 1 );
        methodVisitor.visitMethodInsn( INVOKEVIRTUAL,
                                       ENUM_DICTIONARY_TYPE,
                                       "valueForEnum",
                                       "(Ljava/lang/Enum;)I" );

        NATIVE_INT.writeSet( methodVisitor );

        methodVisitor.visitInsn( RETURN );
    }

    /** {@inheritDoc} */
    @Override
    void allocateFields( final ClassWriter classWriter,
                         final String implName,
                         final Map< String, StructureField > createdFields )
    {
        super.allocateFields( classWriter, implName, createdFields );

        String baseFieldName = "ENUM_DICTIONARY_" + getJavaType().getName().replace( '.', '_' );

        ObjectReference< String > ref = ObjectReference.wrap( (String) null );
        this.setDictionaryField = StructureFieldOrParameter.Helper.findShareableField( createdFields,
                                                                                       this,
                                                                                       baseFieldName,
                                                                                       ref );

        this.dictionaryField = ref.getValue();

        if ( this.setDictionaryField )
        {
            classWriter.visitField( ACC_PRIVATE | ACC_FINAL | ACC_STATIC,
                                    this.dictionaryField,
                                    "L" + ENUM_DICTIONARY_TYPE + ";",
                                    null,
                                    null );
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    void writeStaticInitialiserFragment( final MethodVisitor methodVisitor, final String implName )
    {
        if ( this.setDictionaryField )
        {
            methodVisitor.visitLdcInsn( Type.getType( getJavaType() ) );
            methodVisitor.visitMethodInsn( INVOKESTATIC,
                                           ENUM_DICTIONARY_TYPE,
                                           "get",
                                           "(Ljava/lang/Class;)L" + ENUM_DICTIONARY_TYPE + ";" );
            methodVisitor.visitFieldInsn( PUTSTATIC,
                                          implName,
                                          this.dictionaryField,
                                          "L" + ENUM_DICTIONARY_TYPE + ";" );
        }
    }


}
